#include "annulerrefaire.h"

AnnulerRefaire::AnnulerRefaire(Arbre * arbre)
{
    this->monArbre = arbre;
}


// permet d'annuler
bool AnnulerRefaire::annuler()
{
    // on securise la fonction
    if(!this->onPeutAnnuler()){
        return false;
    }
    // on prend de la pile annuler
    ElementHistorique * h = this->pileAnnuler.pop();
    Noeud * n;
    // on remet l'etat
    switch(h->type)
    {
    case ElementHistorique::TYPE_AJOUT:
        // on doit supprimer un noeud
        this->supprimerNoeud(h->noeud);

        break;
    case ElementHistorique::TYPE_MODIFICATION:
        // on doit remodifier le noeud
        n = this->monArbre->getNoeudAvecID(h->noeud->monID);
        n->inverserAvec(h->noeud);

        break;
    case ElementHistorique::TYPE_SUPPRESSION:
        // on doit ajouter le noeud
        this->ajouteUnNoeud(h->noeud);

        break;
    }

    // on place dans la pile refaire
    this->pileRefaire.push(h);

    return true;
}

// permet de refaire
bool AnnulerRefaire::refaire()
{
    // on securise la fonction
    if(!this->onPeutRefaire()){
        return false;
    }
    // on prend de la pile refaire
    ElementHistorique * h = this->pileRefaire.pop();
    Noeud * n;
    switch(h->type)
    {
    case ElementHistorique::TYPE_AJOUT:
        // on doit ajouter le noeud
        this->ajouteUnNoeud(h->noeud);

        break;
    case ElementHistorique::TYPE_MODIFICATION:
        // on doit remodifier le noeud
        n = this->monArbre->getNoeudAvecID(h->noeud->monID);
        n->inverserAvec(h->noeud);

        break;
    case ElementHistorique::TYPE_SUPPRESSION:
        // on doit supprimer un noeud
        this->supprimerNoeud(h->noeud);

        break;
    }

    // on place dans la pile annuler
    this->pileAnnuler.push(h);

    return true;
}

//memorise une nouvelle action
void AnnulerRefaire::memoriser(Noeud * n, unsigned int type)
{
    // on memorise la nouvelle action
    ElementHistorique * h = new ElementHistorique(n, type);
    this->pileRefaire.push(h);
    this->refaire();
    // on vide la pile de refaire
    this->pileRefaire.clear();
}

bool AnnulerRefaire::onPeutAnnuler()
{
    return !this->pileAnnuler.isEmpty();
}

bool AnnulerRefaire::onPeutRefaire()
{
    return !this->pileRefaire.isEmpty();
}


void AnnulerRefaire::supprimerNoeud(Noeud * noeud)
{
    Noeud * pere = noeud->pere;
    // on casse le lien pere->fils
    pere->removeFils(noeud);
    // et on maj les compteurs
    int nouveauNoLigne = pere->getNoLigne() +1;
    this->monArbre->compteurNoeudsParLigne[nouveauNoLigne] --;
    QList<Noeud *> lf = noeud->fils;
    QList<Noeud *> lf2;
    int c;
    while(!lf.isEmpty())
    {
        lf2.clear();
        c = 0;
        foreach(Noeud * f, lf)
        {
            c++;
            lf2.append(f->fils);
        }
        this->monArbre->compteurNoeudsParLigne[++nouveauNoLigne] -= c;
        lf = lf2;
    }

    // changement de type des ancetres au besoin
    this->monArbre->majTypesAncetres(pere);
}

// precondition: nouveauNoeud.pere est defini
void AnnulerRefaire::ajouteUnNoeud(Noeud * nouveauNoeud)
{
    Noeud * pere = nouveauNoeud->pere;
    if(pere->getNoLigne() == (Arbre::MAXLIGNES-1)){
        return;
    }

    // on ajoute le nouveau fils au pere
    pere->addFils(nouveauNoeud);
    // on augmente le nombre de noeuds dans la ligne du nouveau noeud
    int nouveauNoLigne = pere->getNoLigne() +1;
    this->monArbre->compteurNoeudsParLigne[nouveauNoLigne] ++;
    // et on definie le numero de ligne de ce nouveau noeud
    nouveauNoeud->setNoLigne(nouveauNoLigne);

    // si le nouveau noeud a des fils, les no de lignes sont def
    // on maj les compteurs
    QList<Noeud *> lf = nouveauNoeud->fils;
    QList<Noeud *> lf2;
    int c;
    while(!lf.isEmpty())
    {
        lf2.clear();
        c = 0;
        foreach(Noeud * f, lf)
        {
            c++;
            lf2.append(f->fils);
        }
        this->monArbre->compteurNoeudsParLigne[++nouveauNoLigne] += c;
        lf = lf2;
    }

    // changement de type des ancetres au besoin
    this->monArbre->majTypesAncetres(pere);
}
